#pragma once

#include "SFML/Graphics.hpp"

enum TileType {
	Empty = 0,
	Sand = 0x1,
	Rock = 0x1 << 1,
	Water = 0x1 << 2
};

struct Tile {
	TileType type = TileType::Empty;
};

class Pixel : public sf::Drawable {
public:
	Pixel(int unit, sf::Vector2i pos = { 0,0 }, sf::Color col = sf::Color::Yellow)
		:unit(unit), vert(pos, col, unit) {
	}

	void setpos(const sf::Vector2i& pos) {
		vert.lt.position = (sf::Vector2f)(pos * unit);
		vert.lb.position = (sf::Vector2f)(pos * unit + sf::Vector2i(0, unit));
		vert.rb.position = (sf::Vector2f)(pos * unit + sf::Vector2i(unit, unit));
		vert.rt.position = (sf::Vector2f)(pos * unit + sf::Vector2i(unit, 0));
	}

	void setpos(const sf::Vector2f& pos, int scale) {
		int scale_unit = scale * unit;
		vert.lt.position = (pos);
		vert.lb.position = (pos + sf::Vector2f(0, scale_unit));
		vert.rb.position = (pos + sf::Vector2f(scale_unit, scale_unit));
		vert.rt.position = (pos + sf::Vector2f(scale_unit, 0));
	}

	void settype(TileType type) {
		sf::Color col;
		switch (type) {
		case TileType::Empty: col = sf::Color::White; break;
		case TileType::Rock: col = sf::Color(62, 62, 62); break;
		case TileType::Water: col = sf::Color(0, 122, 204); break;
		case TileType::Sand: col = sf::Color::Yellow; break;
		}
		for (int i = 0; i < 4; i++)
			vert.vert[i].color = col;
	}

	const int unit;

private:

	struct PixelVert {
		union {
			sf::Vertex vert[4];
			struct {
				sf::Vertex lt;
				sf::Vertex lb;
				sf::Vertex rb;
				sf::Vertex rt;
			};
		};

		PixelVert(sf::Vector2i pos, sf::Color col, int unit) {
			lt.position = (sf::Vector2f)(pos * unit);
			lb.position = (sf::Vector2f)(pos * unit + sf::Vector2i(0, unit));
			rb.position = (sf::Vector2f)(pos * unit + sf::Vector2i(unit, unit));
			rt.position = (sf::Vector2f)(pos * unit + sf::Vector2i(unit, 0));
			for (int i = 0; i < 4; i++)
				vert[i].color = col;
		}
	}vert;

	virtual void draw(sf::RenderTarget& target, sf::RenderStates states) const override {
		target.draw(vert.vert, 4, sf::PrimitiveType::Quads, states);
	}
};

class SandMap : public sf::Drawable {
public:
	SandMap(const sf::Vector2i& size) :pixel(tileunit) {
		tilemap.resize((size_t)size.x / tileunit);
		for (auto& raw : tilemap)
			raw.resize((size_t)size.y / tileunit);
	}

	void update() {
		int width = tilemap.size(), height = tilemap[0].size();
		int rd = rand_1000(0,1);
		if (rd) {
			for (int x = 0; x < width; x++) {
				for (int y = height - 1; y >= 0; y--) {
					switch (tilemap[x][y].type) {
					case ::Empty: break;
					case ::Rock:  break;
					case ::Water: update_water(x, y, 0, 0, width, height); break;
					case ::Sand:  update_sand(x, y, 0, 0, width, height); break;
					}
				}
			}
		}
		else {
			for (int x = width - 1; x >= 0; x--) {
				for (int y = height - 1; y >= 0; y--) {
					switch (tilemap[x][y].type) {
					case ::Empty: break;
					case ::Rock:  break;
					case ::Water: update_water(x, y, 0, 0, width, height); break;
					case ::Sand:  update_sand(x, y, 0, 0, width, height); break;
					}
				}
			}
		}
	}

	void setmap(int x, int y, TileType type, int scale) {
		for (int i = 0; i < scale; i++) {
			for (int j = 0; j < scale; j++) {
				if (x >= 0 && x + i <= tilemap.size() - 1 &&
					y >= 0 && y + j <= tilemap[0].size() - 1)
					tilemap[x + i][y + j].type = type;
			}
		}
	}

	const int tileunit = 2;

private:
	std::vector<std::vector<Tile>> tilemap;
	mutable Pixel pixel;

	int rand_1000(int min, int max) {
		int rt = rand() % 1000;
		int un = 1000 / (max - min);
		for (int i = max; i >= min; i--) {
			if (rt >= un * i) {
				return i - min;
			}
		}
	}

	void update_water(int x, int y, int xstart, int ystart, int xend, int yend) {
		int rd = rand_1000(0, 4);
		int r = rd ? 1 : -1;
		int l = -r;
		int u = rd;

		if (isempty(x, y + u, xstart, ystart, xend, yend)) {
			tilemap[x][y].type = Empty;
			tilemap[x][y + u].type = Water;
		}
		else if (isempty(x + l, y, xstart, ystart, xend, yend)) {
			tilemap[x][y].type = Empty;
			tilemap[x + l][y].type = Water;
		}
		else if (isempty(x + r, y, xstart, ystart, xend, yend)) {
			tilemap[x][y].type = Empty;
			tilemap[x + r][y].type = Water;
		}
		else if (isempty(x + l, y + u, xstart, ystart, xend, yend)) {
			tilemap[x][y].type = Empty;
			tilemap[x + l][y + u].type = Water;
		}
		else if (isempty(x + r, y + u, xstart, ystart, xend, yend)) {
			tilemap[x][y].type = Empty;
			tilemap[x + r][y + u].type = Water;
		}

	}

	void update_sand(int x, int y, int xstart,int ystart,int xend, int yend) {
		int rd = rand_1000(0, 4);
		int r = rd ? 1 : -1;
		int l = -r;
		int u = rd;

		if (isempty(x, y + u, xstart, ystart, xend, yend)) {
			tilemap[x][y].type = Empty;
			tilemap[x][y + u].type = Sand;
		}
		else if (isempty(x + l, y + u, xstart, ystart, xend, yend)) {
			tilemap[x][y].type = Empty;
			tilemap[x + l][y + u].type = Sand;
		}
		else if (isempty(x + r, y + u, xstart, ystart, xend, yend)) {
			tilemap[x][y].type = Empty;
			tilemap[x + r][y + u].type = Sand;
		}
		else if (iswater(x+l, y + u, xstart, ystart, xend, yend)) {
			tilemap[x][y].type = Water;
			tilemap[x + l][y + u].type = Sand;
		}
		else if (iswater(x+r, y + u, xstart, ystart, xend, yend)) {
			tilemap[x][y].type = Water;
			tilemap[x + r][y + u].type = Sand;
		}

	}

	virtual void draw(sf::RenderTarget& target, sf::RenderStates states) const override {

		for (int x = 0; x < tilemap.size(); x++) {
			for (int y = 0; y < tilemap[0].size(); y++) {
				if (tilemap[x][y].type != TileType::Empty) {
					pixel.setpos(sf::Vector2i(x, y));
					pixel.settype(tilemap[x][y].type);
					target.draw(pixel);
				}
			}
		}
	}

	bool isempty(int x, int y, int xstart, int ystart, int xend, int yend) {
		if (x >= xstart && x < xend && y >= ystart && y < yend) {
			return tilemap[x][y].type == Empty;
		}
		return false;
	}

	bool issand(int x, int y, int xstart, int ystart, int xend, int yend) {
		if (x >= xstart && x < xend && y >= ystart && y < yend) {
			return tilemap[x][y].type == Sand;
		}
		return false;
	}

	bool iswater(int x, int y, int xstart, int ystart, int xend, int yend) {
		if (x >= xstart && x < xend && y >= ystart && y < yend) {
			return tilemap[x][y].type == Water;
		}
		return false;
	}

};
